package cn.elead.chaos.framework.web.global.response;

import java.lang.reflect.Type;
import java.util.Objects;

import org.springframework.core.MethodParameter;
import org.springframework.http.HttpInputMessage;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.mvc.method.annotation.RequestBodyAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;

import cn.elead.chaos.core.constant.APICons;
import cn.elead.chaos.framework.util.RequestUtils;
import cn.elead.chaos.framework.web.global.ApiResponse;
import cn.hutool.http.HttpStatus;

@ControllerAdvice(annotations = { RestController.class, Controller.class })
public class ApiControllerAdvice implements RequestBodyAdvice, ResponseBodyAdvice<Object> {

	@Override
	public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
		Controller controller = returnType.getDeclaringClass().getAnnotation(Controller.class);
		RestController restController = returnType.getDeclaringClass().getAnnotation(RestController.class);
		ResponseBody responseBody = returnType.getMethodAnnotation(ResponseBody.class);
		boolean wrapper = true;
		UnifiedReturn unifiedReturn = returnType.getMethodAnnotation(UnifiedReturn.class);
		if (Objects.nonNull(unifiedReturn)) {
			wrapper = unifiedReturn.wrapper();
		}
		return (Objects.nonNull(restController) || (Objects.nonNull(controller) && Objects.nonNull(responseBody))) && wrapper;
	}

	@Override
	public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request,
			ServerHttpResponse response) {
		UnifiedReturn unifiedReturn = returnType.getExecutable().getDeclaringClass().getAnnotation(UnifiedReturn.class);
		if (Objects.isNull(unifiedReturn)) {
			unifiedReturn = returnType.getMethodAnnotation(UnifiedReturn.class);
		}
		int status = HttpStatus.HTTP_OK;
		if (Objects.nonNull(unifiedReturn)) {
			status = unifiedReturn.status();
		}
		// ResponseStatus
		ResponseStatus responseStatus = returnType.getMethodAnnotation(ResponseStatus.class);
		if (Objects.nonNull(responseStatus)) {
			status = responseStatus.code().value();
		}
		if(Objects.nonNull(unifiedReturn) && !unifiedReturn.wrapper()) {
			return body;
		}
		return body instanceof ApiResponse ? body : ApiResponse.<Object>success(response, status, body);
	}

	@Override
	public boolean supports(MethodParameter methodParameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {
		return true;
	}

	@Override
	public HttpInputMessage beforeBodyRead(HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {
		return inputMessage;
	}

	@Override
	public Object afterBodyRead(Object body, HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {
		RequestUtils.getRequest().setAttribute(APICons.API_REQUEST_BODY, body);
		return body;
	}

	@Override
	public Object handleEmptyBody(Object body, HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {
		return body;
	}

}
